package org.zjt.chapter1.io;

import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.util.List;

/**
 * Description:
 *
 *      EventLoopGroup 的复用 :
 *
 *          在server中发送信息给下游，netty client 复用 server的eventLoopGroup。
 *
 *
 *      编码器、解码器必须定义在 处理 event handler 之前。
 *
 * @author juntao.zhang
 * Date: 2018-07-13 下午6:43
 * @see
 */
@Slf4j
public class SharedEventLoopGroupTest {

    @Test
    public void ioConvert() {
        EventLoopGroup bossEventExecutors = new NioEventLoopGroup(1);

        EventLoopGroup workEventExecutors = new NioEventLoopGroup();



        try {
            ChannelFuture channelFuture = new ServerBootstrap()
                    .group(bossEventExecutors, workEventExecutors)
                    .channel(NioServerSocketChannel.class)
                    .handler(new ChannelInboundHandlerAdapter() {
                        @Override
                        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                            log.info("server channel 处理连接, msg:{}", msg);
                            ctx.fireChannelRead(msg);
                        }
                    })
                    .childHandler(new SimpleChannelInboundHandler<ByteBuf>() {

                        private ChannelFuture connect;
                        @Override
                        public void channelActive(ChannelHandlerContext ctx) throws Exception {

                            log.info(workEventExecutors.toString()+ bossEventExecutors.toString());

                            // 复用eventLoopGroup
                            connect = new Bootstrap().group(ctx.channel().eventLoop()).channel(NioSocketChannel.class)
                                    .handler(new SimpleChannelInboundHandler<Object>() {
                                        @Override
                                        protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
                                            log.info("channelActive server child channel 处理连接, msg:{}", msg);
                                        }
                                    })
                                    .connect("wwww.baidu.com", 443);
                        }

                        @Override
                        protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
                            log.info("server child channel 处理连接, msg:{}", msg);

                            if (connect.isDone()) {
                                connect.channel().writeAndFlush("zhang");
                            }

                        }
                    }).bind(8080);

            channelFuture.sync();

            channelFuture.channel().closeFuture().sync();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            bossEventExecutors.shutdownGracefully();
            workEventExecutors.shutdownGracefully();
        }
    }


    @Test
    public void ioConvertClient() {
        EventLoopGroup eventExecutors = new NioEventLoopGroup();
        try {

            ChannelFuture channelFuture = new Bootstrap()
                    .group(eventExecutors)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, Boolean.FALSE)
                    .option(ChannelOption.SO_KEEPALIVE, Boolean.TRUE)
                    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();

                            // 解码器
                            pipeline.addFirst(new ByteToMessageDecoder() {
                                @Override
                                protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
                                    byte[] bytes = new byte[in.readableBytes()];
                                    in.readBytes(bytes);
                                    out.add(new String(bytes));
                                }
                            });

                            // 发送编码器
                            pipeline.addLast(new MessageToByteEncoder<String>() {
                                @Override
                                protected void encode(ChannelHandlerContext ctx, String msg, ByteBuf out) throws Exception {
                                    out.writeBytes(msg.getBytes());
                                }
                            });


                            // 接收器
                            pipeline.addLast(new SimpleChannelInboundHandler<Object>() {
                                @Override
                                protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
                                    log.info("ioConvertClient msg:{}", String.valueOf(msg));
                                }

                                @Override
                                public void channelActive(ChannelHandlerContext ctx) throws Exception {
                                    ctx.writeAndFlush("zhangshshs");
                                }
                            });


                        }
                    }).connect("localhost", 8080);

            channelFuture.sync();

            channelFuture.channel().closeFuture().sync();


        }catch (Exception e){
            e.printStackTrace();
        }finally {
            eventExecutors.shutdownGracefully();
        }

    }

}
